#!/usr/bin/env python3
import re
import json

def parse_mbti_text_and_scoring(filepath):
    with open(filepath, 'r', encoding='utf-8') as f:
        content = f.read()

    # Normalize content
    content = content.replace('？', '?').replace('Ａ', 'A').replace('Ｂ', 'B').replace('．', '.')
    content = content.replace('\014', '\n') # Replace form feed with newline for consistency

    # --- Part 1: Parse Questions --- 
    questions_data = []
    # Isolate question text (from start of first question to "==========答题结束==========")
    question_section_match = re.search(r"(1、.*?)(==========答题结束==========)", content, re.DOTALL)
    if not question_section_match:
        print("Error: Could not find the main question section.")
        return []
    
    question_text_block = question_section_match.group(1)
    # Split into individual question blocks. Handles numbers followed by '、'
    # Some questions might be multi-line before options start.
    raw_question_blocks = re.split(r'\n(?=\d+、)', question_text_block)
    
    temp_questions = {}

    for i, block in enumerate(raw_question_blocks):
        block = block.strip()
        if not block: continue

        q_id_match = re.match(r"^(\d+)、(.*)", block, re.DOTALL)
        if not q_id_match: 
            # If the first block doesn't start with number (e.g. intro text before Q1)
            if i == 0 and not block.startswith("1、"):
                # Check if next block starts with 1
                if len(raw_question_blocks) > 1 and raw_question_blocks[1].strip().startswith("1、"):
                    q_id_match = re.match(r"^(\d+)、(.*)", raw_question_blocks[1].strip(), re.DOTALL)
                    block = raw_question_blocks[1].strip()
                else:
                    print(f"Skipping initial block not starting with a question number: {block[:50]}...")
                    continue        
            else:
                print(f"Warning: Could not parse question ID from block: {block[:50]}...")
                continue

        q_id = int(q_id_match.group(1))
        q_text_full = q_id_match.group(2).strip()
        
        q_main = ""
        opt_a = ""
        opt_b = ""

        if 27 <= q_id <= 73: # Part 2: "A. word1 B. word2"
            match_part2 = re.match(r"A\.\s*(.*?)\s*B\.\s*(.*)", q_text_full, re.DOTALL)
            if match_part2:
                opt_a = match_part2.group(1).strip()
                opt_b = match_part2.group(2).strip()
                q_main = f"选择更倾向的词语： '{opt_a}' 或 '{opt_b}'"
            else:
                q_main = q_text_full # Fallback
                opt_a = "选项 A (请核对)"
                opt_b = "选项 B (请核对)"
                print(f"Warning: Part 2, Q{q_id} options not parsed cleanly: {q_text_full}")
        else: # Part 1 and Part 3
            # Try to find question mark to separate question from options
            # Or separate by the first occurrence of "A."
            parts = q_text_full.split('?', 1)
            if len(parts) == 2:
                q_main = parts[0].strip() + '?'
                options_part = parts[1].strip()
            else:
                a_marker_index = q_text_full.find("A.")
                if a_marker_index != -1:
                    q_main = q_text_full[:a_marker_index].strip()
                    options_part = q_text_full[a_marker_index:].strip()
                else:
                    q_main = q_text_full # Fallback, options might be missing or malformed
                    options_part = ""
                    print(f"Warning: Q{q_id} main text and options separation unclear: {q_text_full}")

            # Extract options A and B from options_part
            option_a_match = re.search(r"A\.\s*(.*?)(?:\n\s*B\.|\s+B\.)", options_part, re.DOTALL)
            option_b_match = re.search(r"B\.\s*(.*)", options_part, re.DOTALL)

            if option_a_match:
                opt_a = option_a_match.group(1).strip()
                # Ensure B's text starts after A's text to avoid capturing A's content in B if B is also in A's text
                if option_a_match.group(0) and option_b_match and options_part.find(option_b_match.group(0)) > options_part.find(option_a_match.group(0)):
                     opt_b = option_b_match.group(1).strip()
                elif not option_b_match: # If B is not found after A
                    print(f"Warning: Q{q_id} option B not found clearly after A. Options part: {options_part}")
                    opt_b = "选项 B (请核对)"
                else: # General B match if specific ordering fails
                    opt_b = option_b_match.group(1).strip()

            elif options_part.startswith("A.") and option_b_match: # A is there, B is there, but regex above failed
                opt_a = options_part.split("B.")[0][2:].strip()
                opt_b = option_b_match.group(1).strip()
            else:
                print(f"Warning: Q{q_id} options A/B not parsed cleanly. Options part: {options_part}")
                opt_a = "选项 A (请核对)"
                opt_b = "选项 B (请核对)"
        
        q_main = re.sub(r'\s+', ' ', q_main).strip()
        opt_a = re.sub(r'\s+', ' ', opt_a).strip()
        opt_b = re.sub(r'\s+', ' ', opt_b).strip()

        temp_questions[q_id] = {
            "id": q_id,
            "text": q_main,
            "option_a_text": opt_a,
            "option_b_text": opt_b,
            "option_a_scores_pole": "", # To be filled by scoring parser
            "option_b_scores_pole": ""
        }
    
    # --- Part 2: Parse Scoring Rules --- 
    dim_map = {
        "X1": "J", "Y1": "J", "Z1": "J", "X2": "P", "Y2": "P", "Z2": "P",
        "X3": "S", "Y3": "S", "Z3": "S", "X4": "N", "Y4": "N", "Z4": "N",
        "X5": "E", "Y5": "E", "Z5": "E", "X6": "I", "Y6": "I", "Z6": "I",
        "X7": "F", "Y7": "F", "Z7": "F", "X8": "T", "Y8": "T", "Z8": "T",
    }

    scoring_table_match = re.search(r"MBTI 性格测试答题卡\s*性格描述：\s*题号\s+(X1.*?)(说明 内倾 Introversion\(I\))", content, re.DOTALL)
    if not scoring_table_match:
        print("Error: Scoring table section not found or regex failed.")
    else:
        table_content = scoring_table_match.group(1)
        table_lines = table_content.strip().split('\n')

        current_col_headers = []
        current_col_spans = [] # List of (start_index, end_index) for each column header

        for line in table_lines:
            line = line.rstrip() # Keep leading spaces for index calculation, remove trailing
            if not line.strip(): continue

            if line.strip().startswith("题号"):
                current_col_headers = []
                current_col_spans = []
                # Find Xd, Yd, Zd column headers and their start positions
                # Example: "题号  X1  X2  X3  X4  X5  X6  X7  X8"
                for m in re.finditer(r"([XYZ]\d)", line):
                    current_col_headers.append(m.group(1))
                    current_col_spans.append(m.start(1))
                # Add end for the last column span
                if current_col_spans: current_col_spans.append(len(line) + 5) # Add a pseudo-end for the last column
                continue
            
            if not current_col_headers: continue # Skip lines if headers not yet defined for this block

            q_num_match = re.match(r"\s*(\d+)", line)
            if q_num_match:
                q_num = int(q_num_match.group(1))
                if q_num not in temp_questions:
                    print(f"Warning: Scoring rule for Q{q_num} found, but question itself not parsed. Skipping.")
                    continue

                try:
                    a_idx = line.find('A')
                    b_idx = line.find('B')
                except ValueError:
                    print(f"Warning: A or B not found in scoring line for Q{q_num}: {line}")
                    continue
                
                assigned_a = False
                assigned_b = False
                for i in range(len(current_col_headers)):
                    col_name = current_col_headers[i]
                    col_start = current_col_spans[i]
                    col_end = current_col_spans[i+1] if (i + 1) < len(current_col_spans) else len(line) + 1

                    if a_idx != -1 and col_start <= a_idx < col_end:
                        if dim_map.get(col_name):
                            temp_questions[q_num]['option_a_scores_pole'] = dim_map[col_name]
                            assigned_a = True
                        else:
                            print(f"Warning: Unknown column name {col_name} for Q{q_num} option A scoring.")
                    
                    if b_idx != -1 and col_start <= b_idx < col_end:
                        if dim_map.get(col_name):
                            temp_questions[q_num]['option_b_scores_pole'] = dim_map[col_name]
                            assigned_b = True
                        else:
                            print(f"Warning: Unknown column name {col_name} for Q{q_num} option B scoring.")
                
                if not (assigned_a and assigned_b):
                    print(f"Warning: Could not fully assign A/B scores for Q{q_num}. A_assigned:{assigned_a}, B_assigned:{assigned_b}. Line: '{line}'")
                    if not assigned_a: temp_questions[q_num]['option_a_scores_pole'] = "ERROR"
                    if not assigned_b: temp_questions[q_num]['option_b_scores_pole'] = "ERROR"
    
    # Finalize questions_data list from temp_questions dict, ensuring order and completeness
    for i in range(1, 94):
        if i in temp_questions:
            q_entry = temp_questions[i]
            if not q_entry['option_a_scores_pole'] or not q_entry['option_b_scores_pole'] or \
               q_entry['option_a_scores_pole'] == "ERROR" or q_entry['option_b_scores_pole'] == "ERROR":
                print(f"Critical Warning: Q{i} is missing valid scoring poles. A_pole: '{q_entry['option_a_scores_pole']}', B_pole: '{q_entry['option_b_scores_pole']}'")
            questions_data.append(q_entry)
        else:
            print(f"Critical Warning: Question {i} was not parsed.")
            # Add a placeholder if needed, or error out
            questions_data.append({"id": i, "text": f"Question {i} - PARSING ERROR", "option_a_text": "Error", "option_b_text": "Error", "option_a_scores_pole": "ERROR", "option_b_scores_pole": "ERROR"})

    return questions_data

def main():
    input_filepath = "/home/ubuntu/mbti_questionnaire.txt"
    output_filepath = "/home/ubuntu/MBTI_Qt_App/parsed_questions_v2.json"
    
    final_questions = parse_mbti_text_and_scoring(input_filepath)

    parsed_count = len(final_questions)
    fully_scored_count = sum(1 for q in final_questions if q.get('option_a_scores_pole') and q.get('option_b_scores_pole') and q.get('option_a_scores_pole') != 'ERROR' and q.get('option_b_scores_pole') != 'ERROR')
    
    print(f"Total questions processed: {parsed_count} (expected 93)")
    print(f"Questions with complete scoring rules: {fully_scored_count}")

    if parsed_count != 93 or fully_scored_count != 93:
        print("Warning: Not all questions were parsed or scored correctly. Please review warnings.")
    else:
        print("All 93 questions parsed and scored successfully.")

    with open(output_filepath, 'w', encoding='utf-8') as f:
        json.dump(final_questions, f, ensure_ascii=False, indent=2)

    print(f"Parsed questions and scoring saved to {output_filepath}")

if __name__ == "__main__":
    main()

